package com.rivues.util.tools;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
import org.apache.zookeeper.server.DatadirCleanupManager;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.pretty.DDLFormatter;

import com.rivues.core.RivuDataContext;
import com.rivues.module.platform.web.model.AnalyzerReport;
import com.rivues.module.platform.web.model.AnalyzerReportModel;
import com.rivues.module.platform.web.model.Auth;
import com.rivues.module.platform.web.model.Cube;
import com.rivues.module.platform.web.model.CubeLevel;
import com.rivues.module.platform.web.model.CubeMeasure;
import com.rivues.module.platform.web.model.CubeMetadata;
import com.rivues.module.platform.web.model.DataDic;
import com.rivues.module.platform.web.model.Database;
import com.rivues.module.platform.web.model.Dimension;
import com.rivues.module.platform.web.model.PublishedCube;
import com.rivues.module.platform.web.model.TableDir;
import com.rivues.module.platform.web.model.TableTask;
import com.rivues.module.platform.web.model.TypeCategory;
import com.rivues.module.platform.web.model.User;
import com.rivues.module.report.web.model.PublishedReport;
import com.rivues.util.RivuTools;
import com.rivues.util.iface.cube.CubeFactory;
import com.rivues.util.iface.report.ReportFactory;
import com.rivues.util.serialize.JSON;

public class ZipTools {
	/**
	 * 添加报表到zip
	 * @param reports
	 * @param path
	 * @param user
	 * @param tabid
	 * @param orgi
	 * @param out
	 * @throws Exception
	 */
	public static List<String> reportsToZip(List<PublishedReport> reports,String path,Boolean reportIsExport,Boolean cubeIsExport,Boolean dsIsExport,Boolean authIsExport,User user,String tabid,String orgi,ZipOutputStream out,List<String> entryNames) throws Exception {
		PublishedReport rpt = null;
		List<Object> diclist = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(TypeCategory.class).add(Restrictions.eq("orgi", orgi)));
		for (int i = 0; i < reports.size(); i++) {
			rpt = reports.get(i);
			AnalyzerReport analyzerReport = rpt.getReport() ;
			if(analyzerReport!=null&&analyzerReport.getModel()!=null&&analyzerReport.getModel().size()>0){
	    		for(AnalyzerReportModel model : analyzerReport.getModel()){
	    			PublishedCube publishedCube = CubeFactory.getCubeInstance().getCubeByID(model.getPublishedcubeid()) ;
	    			if(publishedCube==null)continue;
	    			Cube cube = publishedCube.getCube() ;
	    			
	    			if(cubeIsExport){//是否需要导出模型
		    			Map<Integer, Object> map = ZipTools.getAllParentDic(cube.getTypeid(), diclist, TypeCategory.class);
		    			String cube_path = ZipTools.parentDicToZip(map,TypeCategory.class,"cube/",out,entryNames,orgi);
		    			List<Cube> cubes = new ArrayList<Cube>();
		    			
		    			//
		    			List<CubeMetadata> metaList = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(CubeMetadata.class).add(Restrictions.and(Restrictions.eq("orgi", orgi) , Restrictions.eq("cube", cube.getId())))) ;
		    			cube.getMetadata().clear();
		    			cube.getMetadata().addAll(metaList);
		    			
		    			List<Dimension> dimeasionList = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(Dimension.class).add(Restrictions.and(Restrictions.eq("orgi", orgi) , Restrictions.eq("cubeid", cube.getId()))).addOrder(Order.desc("createtime"))) ;
		    			for (int j = 0; j < dimeasionList.size(); j++) {
		    				List<CubeLevel> levels = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(CubeLevel.class)
		    					.add(Restrictions.eq("orgi", orgi))
		    					.add(Restrictions.eq("dimid", dimeasionList.get(j).getId())));
		    				dimeasionList.get(j).getCubeLevel().clear();
		    				dimeasionList.get(j).getCubeLevel().addAll(levels);
		    			}
		    			cube.getDimension().clear();
		    			cube.getDimension().addAll(dimeasionList);
		    			
		    			List<CubeMeasure> cubeMeasureList = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(CubeMeasure.class).add(Restrictions.and(Restrictions.eq("orgi", orgi) , Restrictions.eq("cubeid", cube.getId())))) ;
		    			cube.getMeasure().clear();
		    			cube.getMeasure().addAll(cubeMeasureList);
		    			cubes.add(cube);
		    			if(authIsExport){
		    				entryNames = ZipTools.authToZip(RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(Auth.class).add(Restrictions.eq("dataid",cube.getId()))), cube_path, out, entryNames, orgi);
		    			}
		    			entryNames = ZipTools.cubesToZip(cubes, cube_path, out,entryNames);
	    			}
	    			//关联元数据导出
	    			if(dsIsExport){
	    				List<Database> databaseList = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(Database.class).add(Restrictions.eq("id", cube.getDb())).add(Restrictions.eq("orgi", orgi))) ;
	    				entryNames = ZipTools.dataSourceToZip(databaseList,cube, out,entryNames,orgi);
	    			}
	    			
	    			//导出模型的所有子集目录以及子集模型
	    			//List<TypeCategory> childList = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(TypeCategory.class).add(Restrictions.eq("parentid",cube.getTypeid())).add(Restrictions.eq("orgi",orgi)));
	    			//ZipTools.cubesAndDicToZip(childList, cube_path, user, tabid, orgi, out);
	    		}
			}
	
			if(reportIsExport){//是否需要导出报表
				if(rpt.getReportcontent()!=null){
					rpt.setReportcontent(URLEncoder.encode(JSON.toJSONString(rpt.getReport())));
				}
				out.putNextEntry(new ZipEntry(new StringBuffer().append(path).append(RivuTools.md5(rpt.getName())).append(".rpt").toString()));
				out.write(JSON.toJSONString(rpt).getBytes()) ;
				if(authIsExport){
    				ZipTools.authToZip(RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(Auth.class).add(Restrictions.eq("resourceid",rpt.getId()))), path, out, entryNames, orgi);
    			}
			}
			
			
			
		}
		return entryNames;
	}
	
	private static String getReportAuthPath(String reportPath){
		return reportPath.replace("report", "reportAuth");
	}
	
	
	
	/**
	 * 添加单个报表目录到zip
	 * @param dic
	 * @param path
	 * @param out
	 * @throws IOException
	 */
	public static void reportDicToZip(DataDic dic,String path,ZipOutputStream out,List<String> entryNames,String orgi) throws IOException {
		out.putNextEntry(new ZipEntry(new StringBuffer().append(path).append(RivuTools.md5(dic.getName())).append("/").toString()));
		out.putNextEntry(new ZipEntry(new StringBuffer().append(path).append(RivuTools.md5(dic.getName())).append("/").append(dic.getId()).append(".rptdic").toString()));
		out.write(JSON.toJSONString(dic).getBytes()) ; 
		ZipTools.authToZip(RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(Auth.class).add(Restrictions.eq("resourceid",dic.getId()))), new StringBuffer().append(path).append(RivuTools.md5(dic.getName())).append("/").toString(), out, entryNames, orgi);
	}
	/**
	 * 添加一个模型目录到zip
	 * @param dic
	 * @param path
	 * @param out
	 * @throws IOException
	 */
	public static void cubeDicToZip(TypeCategory dic,String path,ZipOutputStream out) throws IOException {
		out.putNextEntry(new ZipEntry(new StringBuffer().append(path).append(RivuTools.md5(dic.getName())).append("/").toString()));
		out.putNextEntry(new ZipEntry(new StringBuffer().append(path).append(RivuTools.md5(dic.getName())).append("/").append(dic.getId()).append(".cubdic").toString()));
		out.write(JSON.toJSONString(dic).getBytes()) ; 
	}
	/**
	 * 添加一批模型到zip
	 * @param cubes
	 * @param path
	 * @param out
	 * @throws IOException
	 */
	public static List<String> cubesToZip(List<Cube> cubes,String path,ZipOutputStream out,List<String> entryNames) throws IOException {
		for (int i = 0; i < cubes.size(); i++) {
			Cube cube = cubes.get(i);
			String entryName = null;
//			String entryName = new StringBuffer().append(path).append(RivuTools.md5(cube.getName())).append(".cub").toString();
//			if(ZipTools.isExistInList(entryNames, entryName)){
//				continue;
//			}
//			entryNames.add(entryName);
//			out.putNextEntry(new ZipEntry(entryName));
//			out.write(JSON.toJSONString(cube).getBytes()) ; 
			List<PublishedCube> p_cubes =  RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(PublishedCube.class).add(Restrictions.eq("dataid", cube.getId())));
			for (PublishedCube publishedCube : p_cubes) {
				entryName = new StringBuffer().append(path).append(RivuTools.md5(publishedCube.getName())+"_"+(publishedCube.getDataflag()==null?0:publishedCube.getDataflag())).append(".pcub").toString();
				if(ZipTools.isExistInList(entryNames, entryName)){
					continue;
				}
				entryNames.add(entryName);
				out.putNextEntry(new ZipEntry(entryName));
				out.write(JSON.toJSONString(publishedCube).getBytes()) ; 
			}
		}
		return entryNames;
	}
	
	private static boolean isExistInList(List<String> entryNames,String entryName){
		boolean flag = false;
		for (int i = 0; i < entryNames.size(); i++) {
			if(entryName.equals(entryNames.get(i))){
				flag = true;
				break;
			}
		}
		
		return flag;
	}
	
	/**
	 * 获取所有的父级目录
	 * @param id
	 * @param list
	 * @param clazz
	 * @return
	 */
	public static Map<Integer, Object> getAllParentDic(String id,List<Object> list,Class<?> clazz){
		Map<Integer, Object> map = new HashMap<Integer, Object>();
		int index = 0;
		Object parent = null ;
		try {
			while(parent==null){
				for(Object type : list){
					Method getId = clazz.getDeclaredMethod("getId");
					Method getParentid = clazz.getDeclaredMethod("getParentid");
					if(getId.invoke(type).equals(id)){
						parent = type ;
						id = getParentid.invoke(parent).toString();
						break ;
					}
				}
				if(parent!=null){
					map.put(index, parent);
					index++;
					parent = null;
				}else{
					break ;
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		return map;
	}
	/**
	 * 把父级目录添加到zip
	 */
	public static String parentDicToZip(Map<Integer, Object> map,Class<?> clazz,String basepath,ZipOutputStream out,List<String> entryNames,String orgi) throws IOException {

		StringBuffer path = new StringBuffer(basepath);
		try {
			Method getId = clazz.getDeclaredMethod("getId");
			Method getName = clazz.getDeclaredMethod("getName");
			for (int i = map.size()-1; i >=0; i--) {
				Object obj = map.get(i);
				String entryName = new StringBuffer().append(path).append(RivuTools.md5(getName.invoke(obj).toString())).append("/").append(getId.invoke(obj)).append(clazz.getSimpleName().equals("DataDic")?".rptdic":".cubdic").toString();
				if(ZipTools.isExistInList(entryNames, entryName)){
					continue;
				}
				
				entryNames.add(entryName);
				out.putNextEntry(new ZipEntry(new StringBuffer().append(path).append(RivuTools.md5(getName.invoke(obj).toString())).append("/").toString()));
				out.putNextEntry(new ZipEntry(entryName));
				out.write(JSON.toJSONString(obj).getBytes()) ; 
				path.append(RivuTools.md5(getName.invoke(obj).toString())).append("/");
				//if(entryName.endsWith(".rptdic"))createDicAuth(out,path,String.valueOf(getId.invoke(obj)),orgi);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		return path.toString();
	}
	
	private static List<String> authToZip(List<Auth> authList,String path,ZipOutputStream out,List<String> entryNames,String orgi) throws IOException{
		if(authList==null||authList.size()==0)return entryNames;
		for (int i = 0; i < authList.size(); i++) {
			Auth auth = authList.get(i);
			String entryName = new StringBuffer().append(path).append(RivuTools.md5(auth.getId())).append(".auth").toString();
			if(ZipTools.isExistInList(entryNames, entryName)){
				continue;
			}
			entryNames.add(entryName);
			out.putNextEntry(new ZipEntry(entryName));
			out.write(JSON.toJSONString(auth).getBytes()) ; 
			
		}
		return entryNames;
	}
	/**
	 * 添加数据源到zip
	 * @param dbs
	 * @param out
	 * @throws IOException
	 */
	public static List<String> dataSourceToZip(List<Database> dbs,Cube cube,ZipOutputStream out,List<String> entryNames,String orgi) throws IOException {
		for (int i = 0; i < dbs.size(); i++) {
			Database db = dbs.get(i);
			List<TableDir> tabledirs = new ArrayList<TableDir>();//RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(TableDir.class).add(Restrictions.eq("orgi", orgi)).add(Restrictions.eq("type", RivuDataContext.TableDirType.METADATA.toString()))) ;
	    	List<TableTask> tabletasks = new ArrayList<TableTask>();//RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(TableTask.class).add(Restrictions.eq("orgi", orgi)).add(Restrictions.eq("dbid", db.getId()))) ;
	    	for (CubeMetadata meta : cube.getMetadata()) {
	    		tabletasks.add(meta.getTb());
	    		if(!"0".equals(meta.getTb().getTabledirid())){
	    			tabledirs.add((TableDir)RivuDataContext.getService().getIObjectByPK(TableDir.class, meta.getTb().getTabledirid()));
	    		}
	    		
			}
	    	db.setTabledirs(tabledirs);
	    	db.setTabletasks(tabletasks);
			String entryName = new StringBuffer().append("ds/").append(RivuTools.md5(db.getConnctiontype().equals("jdbc")?db.getName():db.getJndiname())).append(".ds").toString();
			if(ZipTools.isExistInList(entryNames, entryName)){
				continue;
			}
			entryNames.add(entryName);
			out.putNextEntry(new ZipEntry(entryName));
			out.write(JSON.toJSONString(db).getBytes()) ; 
		}
		return entryNames;
	}
	/**
	 * 添加模型和模型目录到zip
	 * @param diclist
	 * @param path
	 * @param user
	 * @param tabid
	 * @param orgi
	 * @param out
	 * @throws Exception
	 */
	public static void cubesAndDicToZip(List<TypeCategory> diclist,String path,User user,String tabid,String orgi,ZipOutputStream out,List<String> entryNames)throws Exception{
		TypeCategory dic = null;
		List<TypeCategory> templist = null;
		List<Cube> cubes = null;
		List<TypeCategory> nowList =  new ArrayList<TypeCategory>();
		String p = null;
		for (int i = 0; i < diclist.size(); i++) {
			
			dic = diclist.get(i);
			p = new StringBuffer().append(path).append(RivuTools.md5(dic.getName())).append("/").toString();
			cubes = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(Cube.class).add(Restrictions.eq("typeid", dic.getId())).add(Restrictions.eq("orgi", orgi)));
			nowList.add(dic);
			entryNames = ZipTools.cubesToZip(cubes, p, out,entryNames);
			ZipTools.cubeDicToZip(dic, path, out);
			
			templist = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(TypeCategory.class).add(Restrictions.eq("parentid",dic.getId())).add(Restrictions.eq("orgi",orgi)));
			if(templist.size()>0){
				
				cubesAndDicToZip(templist, p, user, tabid, orgi, out,entryNames);
			}else{
				continue;
			}
		}
	}
	/**
	 * 添加报表和报表目录到zip
	 * @param diclist
	 * @param path
	 * @param user
	 * @param tabid
	 * @param orgi
	 * @param out
	 * @throws Exception
	 */
	public static void childReportsAndDicToZip(List<DataDic> diclist,String path,Boolean reportIsExport,Boolean cubeIsExport,Boolean dsIsExport,Boolean authIsExport,User user,String tabid,String orgi,ZipOutputStream out,List<String> entryNames)throws Exception{
		DataDic dic = null;
		List<DataDic> templist = null;
		List<PublishedReport> reports = null;
		List<DataDic> nowList =  new ArrayList<DataDic>();
		String p = null;
		for (int i = 0; i < diclist.size(); i++) {
			
			dic = diclist.get(i);
			p = new StringBuffer().append(path).append(RivuTools.md5(dic.getName())).append("/").toString();
			reports = ReportFactory.getReportInstance().getReportList(dic.getId(), orgi, user, tabid,RivuDataContext.ReportTypeEnum.REPORT.toString());
			nowList.add(dic);
			if(reportIsExport){
				entryNames = ZipTools.reportsToZip(reports, p,reportIsExport,cubeIsExport,dsIsExport,authIsExport,user,tabid,orgi, out,entryNames);
				ZipTools.reportDicToZip(dic, path, out,entryNames,orgi);
			}
			templist = RivuDataContext.getService().findAllByCriteria(DetachedCriteria.forClass(DataDic.class).add(Restrictions.eq("parentid",dic.getId())).add(Restrictions.eq("orgi",orgi)));
			if(templist.size()>0){
				childReportsAndDicToZip(templist, p,reportIsExport,cubeIsExport, dsIsExport,authIsExport,user, tabid, orgi, out,entryNames);
			}else{
				continue;
			}
		}
	}
	
	public static void tasksToZip(List<PublishedReport> reports,String path,ZipOutputStream out) throws Exception {
		PublishedReport rpt = null;
		for (int i = 0; i < reports.size(); i++) {
			rpt = reports.get(i);
			if(rpt.getReportcontent()!=null)
				rpt.setReportcontent(URLEncoder.encode(rpt.getReportcontent()));
			
			out.putNextEntry(new ZipEntry(new StringBuffer().append(path).append(rpt.getName()).append(".task").toString()));
			out.write(JSON.toJSONString(rpt).getBytes()) ;
		}
	}
	

}
